import 'dart:io';
import 'package:datetime_util/datetime_util.dart' as dateUtil;
import 'package:string_util/string_util.dart' as strUtil;

LogFormatter _formatter = LogFormatter(false);
LogLevel level = LogLevel.info;
bool _isFullPath = false;
bool log2File = false;
String? logFilePath;
void setFullPath(bool fullPath) {
  if (fullPath == _isFullPath) {
    return;
  }

  _formatter = LogFormatter(fullPath);
  _isFullPath = fullPath;
}

bool isFullPath() {
  return _isFullPath;
}

void t(String? log, {StackTrace? stack, LogFormatter? formatter}) {
  _log(log, formatter ?? _formatter, LogLevel.trace, stack : stack);
}

void d(String? log, {StackTrace? stack, LogFormatter? formatter}) {
  _log(log, formatter ?? _formatter, LogLevel.debug, stack : stack);
}

void i(String? log, {StackTrace? stack, LogFormatter? formatter}) {
  _log(log, formatter ?? _formatter, LogLevel.info, stack : stack);
}

void w(String? log, {StackTrace? stack, LogFormatter? formatter}) {
  _log(log, formatter ?? _formatter, LogLevel.warn, stack : stack);
}

void e(String? log, {StackTrace? stack, LogFormatter? formatter}) {
  _log(log, formatter ?? _formatter, LogLevel.error, stack : stack);
}

void _log(String? log, LogFormatter formatter, LogLevel lv, {StackTrace? stack}) {
  if (level.index <= lv.index) {
    String logStr = _getLogString(log,
        stack == null ? StackTrace.current.toString().split('\n')[2] : stack.toString().split('\n')[0],
        formatter,
        lv);
    print(logStr);

    if (log2File && logFilePath != null) {
      String filePath = '$logFilePath';
      final file = File(filePath);
      file.writeAsStringSync(mode: FileMode.append, '$logStr\n', flush: true);
    }
  }
}

String _getLogString(String? log, String stackLine, LogFormatter formatter, LogLevel lv) {
  return '${formatter.format(stackLine, lv)}: $log';
}

enum LogLevel {
  trace, debug, info, warn, error;
}

class LogFormatter {
  final bool _fullPath;

  bool get isFullPath => _fullPath;

  /// [fullPath]
  LogFormatter(bool fullPath) : _fullPath = fullPath;

  String format(String stackLine, LogLevel lv) {
    _SrcData srcData = _SrcData.parse(stackLine, _fullPath);
    return '${dateUtil.toMsString(DateTime.now())} ${lv.name} '
        '${srcData._type}:${srcData._path}:${srcData._class}.${srcData._method}'
        '${srcData._row == null && srcData._col == null ? '' : '('
        '${srcData._row == null ? '' : '${srcData._row}'}'
        '${srcData._col == null ? '' : ':${srcData._col}'})'}';
  }
}

/// 源码位置数据
class _SrcData {
  final String _type;
  final String _path;
  final String _class;
  final String _method;
  final int? _row;
  final int? _col;

  _SrcData(this._type, this._path, this._class, this._method, this._row, this._col);

  static _SrcData parse(String line, bool fullPath) {
    RegExpMatch? contentMatch = RegExp(r'#(\d+)\s+(.+)').firstMatch(line);
    String content = contentMatch!.group(2)!;  // 第二部分

    // 使用正则表达式进行解析
    RegExpMatch? match = RegExp(r'([^(]+)\s+\(([^)]+)\)').firstMatch(content);

    String classAndMethod = match!.group(1)!;
    String insideParentheses = match.group(2)!;

    // 分割 classAndMethod 以 . 符号
    String classPart;
    String methodPart;
    int idx = classAndMethod.indexOf('.');
    if (idx > -1) {      classPart = classAndMethod.substring(0, idx);
      methodPart = classAndMethod.substring(idx + 1);
    } else {
      classPart = "";
      methodPart = classAndMethod;
    }

    // 解析括号内的部分

    String type;
    String path;
    String? row;
    String? col;

    var parts = insideParentheses.split(':');
    type = parts[0];
    parts.removeAt(0);
    if (parts.length == 2) {
      path = parts[0];
      row = parts[1];
      col = null;
    } else if (parts.length > 2) {
      try {
        int.parse(parts[parts.length - 2]);
        row = parts[parts.length - 2];
        col = parts[parts.length - 1];
        parts.removeRange(parts.length - 2, parts.length);
      } catch (e) {
        row = parts[parts.length - 1];
        col = null;
        parts.removeRange(parts.length - 1, parts.length);
      }

      path = '';
      for (var element in parts) {
        path += '$element:';
      }
      path = path.substring(0, path.length - 1);
    } else {
      path = parts.isNotEmpty ? parts[0] : '';
      row = null;
      col = null;
    }

    return _SrcData(type, path, classPart, methodPart,
        row == null ? null : int.parse(row), col == null ? null : int.parse(col));
  }

  static String _formatPath(String path) {
    List list = path.split('/');
    list.removeWhere((element) => strUtil.isNullEmptyOrBlank(element));
    String ret = '';
    if (list.length <= 3) {
      for (var element in list) {
        ret += '$element/';
      }
    } else {
      // 只取最后3个
      for (int i = 0; i < 3; ++i) {
        ret = '${list[list.length - i - 1]}/$ret';
      }
      ret = '.../$ret';
    }
    return ret.substring(0, ret.length - 1);
  }
}